package com.csun.cmny.provider.listener;

import com.csun.cmny.mqtt.constant.MqttMsgConstants;
import com.csun.cmny.provider.controller.websocket.WebSocketUtils;
import com.csun.cmny.provider.device.csun.camera.entity.CsunCameraConstant;
import com.csun.cmny.provider.device.csun.camera.entity.CsunCameraData;
import com.csun.cmny.provider.device.csun.gateway.entity.CsunGatewayData;
import com.csun.cmny.provider.device.csun.sleep.SleepMessageGateway;
import com.csun.cmny.provider.device.csun.sleep.SleepMessageHandler;
import com.csun.cmny.provider.device.csun.sleep.entity.CsunSleepData;
import com.csun.cmny.provider.device.csun.sleep.entity.MsgSleepReal;
import com.csun.cmny.provider.model.constant.CsunSleepConstant;
import com.csun.cmny.provider.model.constant.DmcConstant;
import com.csun.cmny.provider.model.constant.LifeConstant;
import com.csun.cmny.provider.model.domain.*;
import com.csun.cmny.provider.model.dto.device.DeviceStatus;
import com.csun.cmny.provider.model.enums.device.DeviceStatusEnum;
import com.csun.cmny.provider.model.vo.OldmanBedRoomBuildingVo;
import com.csun.cmny.provider.service.*;
import com.csun.cmny.util.PublicUtil;
import com.csun.cmny.util.RedisKeyUtil;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-03-06 11:40
 * @Description:
 */
@Slf4j
@Component
public class RedisExpirationListener extends KeyExpirationEventMessageListener {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private DmcDeviceAlertService dmcDeviceAlertService;
    @Resource
    private DmcCsunSleepDataService dmcCsunSleepDataService;
    @Resource
    private DmcCsunGatewayBindService dmcCsunGatewayBindService;
    @Resource
    private DmcGatewayDataHiService dmcGatewayDataHiService;
    @Resource
    private DmcGatewayDataService dmcGatewayDataService;
    @Resource
    private DmcDeviceService dmcDeviceService;
    @Resource
    private WebSocketUtils webSocketUtils;
    @Resource
    private SleepMessageGateway sleepMessageGateway;
    @Resource
    private DmcCsunBodyGroupService dmcCsunBodyGroupService;

//    @Resource
//    private SleepMessageHandler sleepMessageHandler;

    public RedisExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {

        // 过期主题 eg: __keyevent@*__:expired
        String topic = new String(pattern);
        // 过期通道 eg: __keyevent@0__:expired
        String channel = new String(message.getChannel());
        // 过期的key eg: cmny:device:csun:gateway:online:Gateway-chisj
        String expiredKey = new String(message.getBody());

        // String expiredKey = message.toString(); 和 message.getBody() 是一样的
        log.info("redis key expired... topic = [{}], channel = [{}], expiredKey = [{}]",
                topic, channel, expiredKey);

        if (expiredKey.startsWith(CsunSleepConstant.ACTION_PREFIX)) {
            String deviceNo = expiredKey.substring(CsunSleepConstant.ACTION_PREFIX_INDEX);
            String redisKey = RedisKeyUtil.getCsunSleepKey(deviceNo);
            CsunSleepData csunSleepData = (CsunSleepData) redisTemplate.opsForValue().get(redisKey);
            log.info("=====> csunSleepData = {}", csunSleepData.toString());
            if (PublicUtil.isNotEmpty(csunSleepData)
                    && csunSleepData.getStatus().equals(DeviceStatusEnum.ONLINE.getKey())
                    && csunSleepData.getInBed().equals(DmcConstant.CSUN_SLEEP_IN_BED)) {
                csunSleepData.setAction(0);
                redisTemplate.opsForValue().set(redisKey, csunSleepData);
                DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
                OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
                Integer result = dmcDeviceAlertService.csunSleepAlertMsg(deviceNo, dmcDeviceAlert, obrvo,
                        CsunSleepConstant.ACTION_OVER);
                log.info("=====> dmcDeviceAlert = {}", dmcDeviceAlert.toString());
                if (result > 0) {
                    dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                    dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
                }
            }
        }

        if (expiredKey.startsWith(RedisKeyUtil.CSUN_GATEWAY_ONLINE)) {  // 网关是否在线 - 设备过期
            // 获取网关编号
            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_GATEWAY_ONLINE.length() + 1);
            String redisKey = RedisKeyUtil.getCsunGatewayKey(deviceNo);
            log.info("网关设备离线 deviceNo = {}", deviceNo);
            CsunGatewayData csunGatewayData = (CsunGatewayData) redisTemplate.opsForValue().get(redisKey);
            if (PublicUtil.isNotEmpty(csunGatewayData) &&
                    DeviceStatusEnum.ONLINE.getKey().equals(csunGatewayData.getStatus())) {
                // 离线
                csunGatewayData.setStatus(DeviceStatusEnum.OFFLINE.getKey());
                redisTemplate.opsForValue().set(redisKey, csunGatewayData);
                DmcGatewayDataHi dmcGatewayDataHi = new DmcGatewayDataHi();
                dmcGatewayDataHi.setMsgType(MqttMsgConstants.INVITE_CLOSE);
                dmcGatewayDataHi.setDeviceId(csunGatewayData.getDeviceId());
                dmcGatewayDataHi.setStatus(csunGatewayData.getStatus());
                dmcGatewayDataHi.setNetgateNo(deviceNo);
                dmcGatewayDataHiService.save(dmcGatewayDataHi);

                DmcGatewayData dmcGatewayData = new DmcGatewayData();
                dmcGatewayData.setMsgType(MqttMsgConstants.INVITE_CLOSE);
                dmcGatewayData.setDeviceId(csunGatewayData.getDeviceId());
                dmcGatewayData.setStatus(csunGatewayData.getStatus());
                dmcGatewayData.setNetgateNo(deviceNo);
                dmcGatewayDataService.update(dmcGatewayData);

                /*
                 * 2020-03-12 xcr
                 * 离线报警
                 * */
                DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
                OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
                int i = dmcDeviceAlertService.deviceOfflineAlert(deviceNo, dmcDeviceAlert, obrvo);
                if(i > 0){
                    dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                    dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
                }

                /**
                 * 网关上面传感器的不一定只和一个网管绑定
                 *
                 * 比如温湿度计H绑定了A,B两个网关，仅A挂了，网关B和温湿度计H是正常的
                 */
                //查询网关绑定的设备
//                GatewayBindDto dto = new GatewayBindDto();
//                dto.setGatewayId(csunGatewayData.getDeviceId());
//                List<GatewayBindVo> gatewayBindVos = dmcCsunGatewayBindService.listBindDevice(dto);
//                //将网关绑定的设备判定为离线
//                for (GatewayBindVo vo : gatewayBindVos) {
//                    dmcDeviceAlertService.deviceOfflineAlert(vo.getDeviceNo());
//                }
//                log.info("网关设备离线 csunGatewayData = {}", csunGatewayData);
            }
        }

        if (expiredKey.startsWith(RedisKeyUtil.CSUN_SLEEP_ONLINE)) { // 床垫是否在线 - 设备过期

            // 获取网关编号
            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_SLEEP_ONLINE.length() + 1);
            String redisKey = RedisKeyUtil.getCsunSleepKey(deviceNo);
            log.info("床垫设备离线 deviceNo = {}" + deviceNo);
            CsunSleepData csunSleepData = (CsunSleepData) redisTemplate.opsForValue().get(redisKey);
            if (PublicUtil.isNotEmpty(csunSleepData) &&
                    DeviceStatusEnum.ONLINE.getKey().equals(csunSleepData.getStatus())) {
                csunSleepData.setStatus(DeviceStatusEnum.OFFLINE.getKey());
                csunSleepData.setInBed(0);
                csunSleepData.setHeartbeat(-1);
                csunSleepData.setBreath(-1);
                csunSleepData.setAction(1);

                DmcCsunSleepData dmcCsunSleepData = new DmcCsunSleepData();
                BeanUtils.copyProperties(csunSleepData, dmcCsunSleepData);
                dmcCsunSleepDataService.update(dmcCsunSleepData);

                redisTemplate.opsForValue().set(redisKey, csunSleepData);

                // 通过设备Id推送通知
                dmcDeviceAlertService.sendSleepDataByDeviceId(csunSleepData.getDeviceId());

                // sleepMessageHandler.publishCsunSleepData(csunSleepData);
                publishCsunSleepData(csunSleepData);

                /*
                 * 2020-03-12 xcr
                 * 离线报警
                 * */
                DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
                OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
                int i = dmcDeviceAlertService.deviceOfflineAlert(deviceNo, dmcDeviceAlert, obrvo);
                if(i > 0){
                    dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                    dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
                }
                // 推送设备状态改变消息
                DeviceStatus deviceStatus = new DeviceStatus();
                deviceStatus.setDeviceId(csunSleepData.getDeviceId());
                deviceStatus.setGroupId(csunSleepData.getGroupId());
                deviceStatus.setDeviceNo(csunSleepData.getDeviceNo());
                deviceStatus.setMessage(String.format("设备%s离线", csunSleepData.getDeviceNo()));
                webSocketUtils.pushWebsocketMessageWithStatus(deviceStatus);

                log.info("床垫设备离线 csunGatewayData = {}" + csunSleepData);
            }
        }

        //如果接收到有离床Key, 说明需要推送离床报警
        if (expiredKey.startsWith(RedisKeyUtil.CSUN_SLEEP_LEAVE_BED)) { // 超过离床报警阈值
            // 获取设备编号
            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_SLEEP_LEAVE_BED.length() + 1);
            log.info("床垫设备离床报警 deviceNo = {}", deviceNo);
            DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
            OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
            int result = dmcDeviceAlertService.csunSleepAlertMsg(deviceNo, dmcDeviceAlert, obrvo, CsunSleepConstant.OFF_BED);
            log.info("=====> dmcDeviceAlert = {}", dmcDeviceAlert.toString());
            if (result > 0) {
                dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
            }
            //移除系统缓存的redisKey
            SleepMessageHandler.expireRedisKeySet.remove(RedisKeyUtil.getCsunSleepLeaveBed(deviceNo));
        }

        if (expiredKey.startsWith(RedisKeyUtil.CSUN_HUMIDISTAT_ONLINE)) {  // 温度计是否在线 - 设备过期
            // 获取温度计
            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_HUMIDISTAT_ONLINE.length() + 1);
            log.info("温度计离线 deviceNo = {}", deviceNo);

            /*
             * 2020-03-12 xcr
             * 离线报警
             * */
            DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
            OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
            int i = dmcDeviceAlertService.deviceOfflineAlert(deviceNo, dmcDeviceAlert, obrvo);
            if(i > 0){
                dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
            }
        }

        if (expiredKey.startsWith(RedisKeyUtil.CSUN_BODYSENSOR_ONLINE)) {  // 人感是否在线 - 设备过期
            // 获取人感编号
            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_BODYSENSOR_ONLINE.length() + 1);
            log.info("人感离线 deviceNo = {}", deviceNo);

            DmcDevice dmcDevice = dmcDeviceService.queryDeviceByNumber(deviceNo);
            if(PublicUtil.isEmpty(dmcDevice)){
                return;
            }

            /*
             * 2020-03-12 xcr
             * 离线报警
             * */
            DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
            OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
            int i = dmcDeviceAlertService.deviceOfflineAlert(deviceNo, dmcDeviceAlert, obrvo);
            if(i > 0){
                dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
            }
            // 推送设备状态改变消息
            DeviceStatus deviceStatus = new DeviceStatus();
            deviceStatus.setDeviceId(dmcDevice.getId());
            deviceStatus.setGroupId(dmcDevice.getGroupId());
            deviceStatus.setDeviceNo(dmcDevice.getDeviceNo());
            deviceStatus.setMessage(String.format("设备%s离线", dmcDevice.getDeviceNo()));
            webSocketUtils.pushWebsocketMessageWithStatus(deviceStatus);
        }

        if (expiredKey.startsWith(RedisKeyUtil.CSUN_CAMERA_ONLINE)) {   // 摄像头是否在线 - 设备过期

            String deviceNo = expiredKey.substring(RedisKeyUtil.CSUN_CAMERA_ONLINE.length() + 1);

            log.info("=====> camera device [{}] is off", deviceNo);

            String redisKey = RedisKeyUtil.getCsunCameraKey(deviceNo);
            CsunCameraData csunCameraData = (CsunCameraData) redisTemplate.opsForValue().get(redisKey);

            if (PublicUtil.isNotEmpty(csunCameraData) &&
                    DeviceStatusEnum.ONLINE.getKey().equals(csunCameraData.getStatus())) {

                csunCameraData.setStatus(DeviceStatusEnum.OFFLINE.getKey());

                redisTemplate.opsForValue().set(redisKey, csunCameraData);

                DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
                OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
                int i = dmcDeviceAlertService.deviceOfflineAlert(deviceNo, dmcDeviceAlert, obrvo);
                if(i > 0){
                    dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                    dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
                }
                // 推送设备状态改变消息
                DeviceStatus deviceStatus = new DeviceStatus();
                deviceStatus.setDeviceId(csunCameraData.getDeviceId());
                deviceStatus.setGroupId(csunCameraData.getGroupId());
                deviceStatus.setDeviceNo(csunCameraData.getDeviceNo());
                deviceStatus.setMessage(String.format("设备%s离线", csunCameraData.getDeviceNo()));
                webSocketUtils.pushWebsocketMessageWithStatus(deviceStatus);

            } else {
                log.info("=====> camera {} is be break.", deviceNo);
            }

            // 判断设备是否还在连接中
            Channel exist = CsunCameraConstant.CAMERA_CHANNEL_MAP.get(deviceNo);
            if (exist != null) {
                exist.close();
            } else {
                log.info("=====> camera channel is null");
            }
        }


        if (expiredKey.startsWith(RedisKeyUtil.CSUN_GATEWAY_BODY_SENSOR_GROUP_ONLINE)) {   // 摄像头是否在线 - 设备过期

            String id = expiredKey.substring(RedisKeyUtil.CSUN_GATEWAY_BODY_SENSOR_GROUP_ONLINE.length() + 1);

            DmcCsunBodyGroup dmcCsunBodyGroup = dmcCsunBodyGroupService.selectByKey(id);
            if (PublicUtil.isEmpty(dmcCsunBodyGroup)) {
                log.error("=====> body group not exist.");
                return ;
            }

            // 根据Id查询
            DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
            OldmanBedRoomBuildingVo obrvo = new OldmanBedRoomBuildingVo();
            int i = dmcDeviceAlertService.deviceBodyGroupAlert(dmcCsunBodyGroup, dmcDeviceAlert, obrvo);
            if(i > 0){
                dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrvo);
                dmcDeviceAlertService.sendLifeMsg(dmcDeviceAlert, obrvo, LifeConstant.YES_SOUND);
            }

        }
    }

    public void publishCsunSleepData(CsunSleepData csunSleepData) {

        MsgSleepReal msgSleepReal = new MsgSleepReal();
        BeanUtils.copyProperties(csunSleepData, msgSleepReal);

        /**
         * @date    2019-05-06
         * @author  chisj
         * @desc    避免一直给设备推送消息
         */
        String topic = "FRONT_CLIENT_" + csunSleepData.getDeviceNo();

        sleepMessageGateway.sendToMqtt(topic, 1,
                JSONObject.fromObject(msgSleepReal).toString());

    }
}
